package tinybase.pf;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;

import tinybase.basic.BytePointer;
import tinybase.basic.IntegerWrapper;
import tinybase.basic.RC;



public class PF_Manager {

    private PF_BufferMgr pBufferMgr;
    public Hashtable<FileDescriptor, PF_FileHandle> openFiles;
    public PF_FileHandle pfFh;

    //Constructeur. Initialise les variables d'instance
    public PF_Manager(){

        pBufferMgr = new PF_BufferMgr(PF_Internal.PF_BUFFER_SIZE);
        openFiles = new Hashtable<FileDescriptor, PF_FileHandle>();

    }

    /*
     * Cette methode permet de creer un fichier pagine de nom filename et
     * d'ecrire les meta-donnees dans l'ente de la premiere page
     * Un fichier portant ce nom ne devrait pas deja exister
     * Params:      String filename: Le nom du fichier a creer
     * Retour:      PF Error code
     */
    public RC createFile(String filename){

        //Creer le fichier ou retoruner un code d'erreur
        //positif si le fichier existe deja
        File f = new File(filename);
        System.out.println(filename);
        if (f.exists())
            return RC.PF_FILEEXIST;

        try {

            f.createNewFile();

            RandomAccessFile ioStream = new RandomAccessFile(f, "rws");

            //Initialisation de l'entete du fichier
            int numPages = 0;
            int firstFree = PageNum.PF_PAGE_LIST_END.getPageNum();
            PF_FileHdr hdr = new PF_FileHdr(firstFree, numPages);

            ioStream.setLength(Pf.PF_PAGE_SIZE);
            //                        int fFree = Integer.parseInt(firstFree.toString());
            //                        int nPages = Integer.parseInt(numPages.toString());
            ioStream.seek(0);
            ioStream.writeInt(firstFree);
            ioStream.writeInt(numPages);
            ioStream.close();

        } catch (FileNotFoundException e) {

            return RC.PF_CANNOTCREATEFILE;

        }catch (IOException e) {

            return RC.PF_CANNOTCREATEFILE;
        }

        return RC.PF_SUCCESS;
    }


    /*
     * Cette methode permet d'ouvrir un fichier pagine de nom filename
     * Un fichier portant ce nom devrait deja exister
     * Params:      String filename: Le nom du fichier a ouvrir
     * Output; Un objet PF_FileHandle pointant sur ce fichier
     * Retour:      PF Error code
     */
    public RC openFile(String filename, PF_FileHandle fileHandle) {

    	RC rc = RC.PF_SUCCESS;
    	if (fileHandle.isFileOpen())
    		return RC.PF_FILEOPEN;
    	
        //On verifie que le fichier existe
        File f = new File(filename);
        /*
        if (!f.exists())
            return ReturnCode.PF_FILENOTFOUND;
            */

        //On verifie que le fichier n'est pas deja ouvert
/*        if (openFiles.contains(fileHandle) && fileHandle.isFileOpen())
            return ReturnCode.PF_FILEOPEN;
*/

        //On ouvre le fichier et on recupere l'objet
        //PF_FileHandle dans la variable pfFh
//        getFileHandle(f);
		try {
			RandomAccessFile rf = new RandomAccessFile(f, "rws");
			rf.seek(0);
			byte[] buf = new byte[8];
			rf.read(buf);
			ByteBuffer bbuf = ByteBuffer.wrap(buf);
			int firstFree = bbuf.getInt(0);
			int numPages = bbuf.getInt(4);
			fileHandle.getFileHeader().setFirstFree(firstFree);
			fileHandle.getFileHeader().setNumPages(numPages);
			System.out.println("Manager.openFile - firstFree = " + firstFree
					+ " ; numPages = " + numPages);

			// On marrque le fichier ouvert
			fileHandle.setDescriptor(rf.getFD());
			fileHandle.setFileOpen(true);
			fileHandle.setHdrChanged(true);
			fileHandle.setBufferMgr(pBufferMgr);
			//openFiles.put(fileHandle.getDescriptor(), fileHandle);

			System.out.println("Openfile success !");

			return RC.PF_SUCCESS;
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
			return RC.PF_HDRREAD;
		}
	}


    /*
     * Cette methode permet de fermer le fichier de nom filename
     * Le fichier devrait avoir ete prealablement ouver par OpenFile()
     * Params:      PF_FileHandle fileHandle: L'objet PF_FileHandle point vers ce fichier
     * Output:
     * Retour:      PF Error code
     */
    public RC closeFile(PF_FileHandle fileHandle) {

//        fileHandle = pfFh;

        //On verifie que le fichier a bien ete ouvert
//        if (!openFiles.contains(fileHandle))
//            return ReturnCode.PF_CLOSEDFILE;
    	if (!fileHandle.isFileOpen())
    		return RC.PF_CLOSEDFILE;
    	
        // On flush toutes les pages de ce fichier du buffer pool et on met a jour le header
        //fileHandle.flushPages();

        //On recupere l'objet FileChanel relatif a ce fichier

        //                FileChannel fc = fileHandle.getFileChanel();
        // FileChannel fc= fileHandle.getFileChannel();
        
        FileDescriptor fd = fileHandle.getDescriptor();
        if (!fileHandle.isFileOpen() || !fd.valid())
        	return RC.PF_CLOSEDFILE;
        
        
        /* if ( fc == null )
            return ReturnCode.PF_CLOSEDFILE; */

        // On ferme le fichier
        try {
        	FileOutputStream out = new FileOutputStream (fd);
            out.close();
            //fc.close();
        } catch (IOException e) {
            return RC.PF_EOF;
        }

        //On marque le fichier fermé
        fileHandle.setFileOpen(false);
        openFiles.remove(fileHandle);

        return RC.PF_SUCCESS;
    }


    //Supprime le fichier de nom filename
    public RC destroyFile(String filename) {

        File f = new File(filename);

        //On verifie que le fichier existe
        if (!f.exists())
            return RC.PF_FILENOTFOUND;

        //On verifie que le fichier n'est pas deja ouvert
//        if (fh.isFileOpen())
//            return ReturnCode.PF_FILEOPEN;

        //On supprime le fichier
        if (!f.delete())
            return RC.PF_FAILEDTODELETEFILE;

        return RC.PF_SUCCESS;

    }



    // ClearBuffer
    //
    // Desc: Remove all entries from the buffer manager.
    //             This routine will be called via the system command and is only
    //             really useful if the user wants to run some performance
    //             comparison starting with an clean buffer.
    // In:   Nothing
    // Out:  Nothing
    // Ret:  Returns the result of PF_BufferMgr::ClearBuffer
    //             It is a code: 0 for success, something else for a PF error.
    //
    public RC clearBuffer()
    {
        return pBufferMgr.clearBuffer();
    }

    // PrintBuffer
    //
    // Desc: Display all of the pages within the buffer.
    //             This routine will be called via the system command.
    // In:   Nothing
    // Out:  Nothing
    // Ret:  Returns the result of PF_BufferMgr::PrintBuffer
    //             It is a code: 0 for success, something else for a PF error.

    public RC printBuffer()
    {
        return pBufferMgr.printBuffer();
    }


    // ResizeBuffer
    //
    // Desc: Resizes the buffer manager to the size passed in.
    //             This routine will be called via the system command.
    // In:   The new buffer size
    // Out:  Nothing
    // Ret:  Returns the result of PF_BufferMgr::ResizeBuffer
    //             It is a code: 0 for success, PF_TOOSMALL when iNewSize
    //             would be too small.

    public RC resizeBuffer(int iNewSize)
    {
        return pBufferMgr.resizeBuffer(iNewSize);
    }

    //------------------------------------------------------------------------------
    // Three Methods for manipulating raw memory buffers.  These memory
    // locations are handled by the buffer manager, but are not
    // associated with a particular file.  These should be used if you
    // want memory that is bounded by the size of the buffer pool.
    //
    // The PF_Manager just passes the calls down to the Buffer manager.
    //------------------------------------------------------------------------------


    public RC getBlockSize(IntegerWrapper length)
    {
        return pBufferMgr.getBlockSize(length);
    }

    //Alloue une page (un block) a un fichier
    public RC allocateBlock(BytePointer buffer)
    {
        return pBufferMgr.allocateBlock(buffer);
    }

    //Supprime une page (un block) d'un fichier
    public RC disposeBlock(byte[] buffer)
    {
        return pBufferMgr.disposeBlock(buffer);
    }


    /*
     * Cette methode initialise et retourne l'objet PF_FileHandle
     * relatif au fichier f en parametre.
     * Params: File f, l'objet File correspondant
     * Return: FileHandle fh
     */
    /*
    private RC getFileHandle(File f) throws IOException{

        PF_BufferMgr pBufMgr = null;
        PF_FileHdr hdr = null;

        //On cree un fileChannel qui pointera vers ce fichier pour
        //des operation de lecture et ecriture futures
        RandomAccessFile ioStream = new RandomAccessFile(f, "rws");
        // FileChannel fc = ioStream.getChannel();

        //On lit l'entete du fichier
        ioStream.seek(0);
        int firstFree = ioStream.readInt();
        int numPages = ioStream.readInt();
        if((new PageNum(firstFree)) == null && 
                (new PageNum(numPages)) == null)
            return RC.PF_HDRREAD;

        hdr = new PF_FileHdr(firstFree, numPages);
        
        System.out.println("hdr.firstFree = " + hdr.firstFree.getPageNum());
        System.out.println("hdr.numPages = " + hdr.numPages.getPageNum());

        //hdr.setNumPages(new PageNum(ioStream.readInt()));
        //hdr.setFirstFree(new PageNum(ioStream.readInt()));

        //On initialise le gestionaire de memoire
        pBufMgr = new PF_BufferMgr(PF_Internal.PF_BUFFER_SIZE);

        FileDescriptor fd = ioStream.getFD();
        pfFh = new PF_FileHandle(pBufMgr, hdr, true, true, fd);

        openFiles.put(fd, pfFh);
        return RC.PF_SUCCESS;

    }*/

}
